


#include "includes.h"

struct bytefile_header
{
  unsigned int magic : 32          __attribute__((packed));
  unsigned int info : 32           __attribute__((packed));
  unsigned int info2 : 32          __attribute__((packed));
  unsigned int function_table : 32 __attribute__((packed));
  unsigned int bytecode : 32       __attribute__((packed));
  unsigned int ft_length : 32      __attribute__((packed));
  unsigned int bc_length : 32      __attribute__((packed));
};

static struct function_info START_FUNCTION = 
  {id: 0, bytecode_address: NULL, compiled_address: NULL, name: "main.start"};
static struct function_info *FUNCTIONS = &START_FUNCTION;
static int function_id = 1;

struct function_info *
function_lookup_by_id(int id)
{
  struct function_info *i = FUNCTIONS;
  
  while(i)
    {
      if (i->id == id)
	{
	  return i;
	}
      i = i->next;
    }

  return NULL;
}


static struct function_info *
function_lookup(char *name)
{
  struct function_info *i = FUNCTIONS;
  
  while (i && i->name)
    {
      if (!strcmp(name, i->name))
	{
	  return i;
	}
      i = i->next;
    }

  return NULL;
}


static struct function_info *
function_add(char *name, void *bc_address, void *cm_address)
{
  struct function_info *i = malloc(sizeof(struct function_info));
  
  i->id = function_id++;
  i->bytecode_address = bc_address;
  i->compiled_address = cm_address;
  i->name = strdup(name);
  i->next = FUNCTIONS;
  FUNCTIONS = i;
  return i;
}

static void
print_functions()
{
  struct function_info *i = FUNCTIONS;
  printf("ID\t Name        \t  Bytecode \t  Compiled\n");
  printf("--\t ----        \t ----------\t ----------\n");
  while(i)
    {
      printf("%d\t %-12s\t 0x%08x\t 0x%08x\n", i->id, i->name, (int) i->bytecode_address, (int) i->compiled_address);
      i = i->next;
    }
}


// These allow resolution of jump points and functions that aren't 
// defined prior to being referenced.
struct jump_entry
{
  int id;
  int offset;
  struct jump_entry *next;
};

struct function_entry
{
  char *name;
  int offset;
  struct function_entry *next;
};


static char * 
load_function(char *code)
{
  char *op = code;
  struct memspace *mem = msp_init(64);
  //char *bytecode = (char *) &mem->data;
  char *fname = NULL;
  struct jump_entry *unresolved = NULL;
  yjk_bcmem *bc = NULL;
  
  int *label_table = malloc(128);
  int label_table_size = 128 / sizeof(int);

  //Clear label table
  int j;
  for (j = 0; j < label_table_size; j++)
    {
      label_table[j] = -1;
    }

  if (*op != DEFUN)
    {
      printf("Error in bytecode: %x\n", *op);
      exit(1);
    }

  op++;
  fname = strdup(op);
  while (*op++); //skip string
    
  int func_end = 0;
  while (!func_end)
    {
      if (*(unsigned char *) op < PUSHINT)
	{
	  bc = (yjk_bcmem *) msp_alloc(&mem, 1);
	  bc->op = *op;
	  op++;
	  continue;
	}

      switch(*(unsigned char *) op)
	{

	case PUSHINT:
	  {
	    char *endptr;
	    op++;
	    unsigned int len = strlen(op) + 1;
	    unsigned int i = strtol(op, &endptr, 16);
	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->op1arg32));
	    bc->op1arg32.op = PUSHINT32;
	    bc->op1arg32.arg = i;
	    op += len;
	    break;
	  }

	case PUSHINT32:
	  {
	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->op1arg32));

	    bc->op1arg32.op  = PUSHINT32;
	    bc->op1arg32.arg = ntohl(((op1arg32 *) op)->arg);

	    op += sizeof(op1arg32);
	    break;
	  }

	case PUSHBIGINT:
	  {
	    op++;
	    YJK_BIGINT *bi = yjk_parse_bigint(op);
	    while(*op++);//skip string

	    printf("Parsed BIGINT: %d ", BIGINT_SIZE(bi));
	    print_bigint(bi);

	    bc = (yjk_bcmem *) msp_alloc(&mem, 1 + BIGINT_SIZE(bi));
	    bc->opbigint.op = PUSHBIGINT;

	    //copy bigint to bytecode
	    copy_bigint(bi, &bc->opbigint.bigint);
	  }
	  break;
	  
	case PUSHSTRING:
	  {
	    int len = strlen(op + 1) + 1;
	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->opstrarg) + len);
	    bc->opstrarg.op = *op;
	    strcpy(bc->opstrarg.arg, op + 1);
	    op += len + 1;
	  }
	  break;

	case PUSHFLOAT:
	  {
	    op++;
	    double n = strtod(op, NULL);
	    while(*op++); //skip string

	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->opdblarg));
	    bc->opdblarg.op = PUSHFLOAT;
	    bc->opdblarg.n = n;
	  }
	  break;

	case LABEL:
	  {
	    op++;
	    unsigned int i = ntohl(*(uint32_t *) op);
	    op += sizeof(uint32_t);

	    while (i >= label_table_size)
	      {
		int x = label_table_size;
		label_table_size += 32;
		label_table = realloc(label_table, label_table_size * sizeof(unsigned int));
		for (; x < label_table_size; x++)
		  {
		    label_table[x] = -1;
		  }
	      }

	    label_table[i] = (int) msp_addr(mem) - (int) &mem->data;
	    break;
	  }
	      
	case JMP:
	case JCOND:
	  {
	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->op1arg32));

	    bc->op1arg32.op = ((op1arg32 *) op)->op;
	    unsigned int i = ntohl(((op1arg32 *) op)->arg);
	    op += sizeof(op1arg32);

	    bc->op1arg32.arg = -1;
	    if (i >= label_table_size || label_table[i] == -1)
	      {
		struct jump_entry *j = malloc(sizeof(struct jump_entry));
		j->id = i;
		j->offset = (int) &bc->op + 1 - (int) &mem->data;
		j->next = unresolved;
		unresolved = j;
	      }
	    else 
	      {
		bc->op1arg32.arg = label_table[i];
	      }
	    break;
	  }

	case CALL:
	  {
	    named_fcall *nf = (void *) op;
	    op += 2; //skip opcode and numargs
	    while(*op++); //skip string argument

	    struct function_info *fi = function_lookup(nf->name);

	    if (!fi)
	      {
		fi = function_add(nf->name, NULL, NULL);
	      }

	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->fcall));
	    bc->fcall.numargs = nf->numargs;

	    if (fi->compiled_address)
	      {
		bc->fcall.op   = CALLCP;
		bc->fcall.addr = (int) fi->compiled_address;
	      }
	    else if (fi->bytecode_address)
	      {
		bc->fcall.op   = CALLBC;
		bc->fcall.addr = (int) fi->bytecode_address;
	      }
	    else 
	      {
		bc->fcall.op   = CALL;
		bc->fcall.addr = fi->id;
	      }

	    break;
	  }

	case TAILCALL:
	  {
	    named_tcall *nt = (named_tcall *) op;
	    op += 3; //skip opcode, num calling args, and numargs
	    while(*op++); //skip string argument

	    printf("Tail call: %d %d %s\n", nt->ncallargs, nt->numargs, nt->name);
	    struct function_info *fi = function_lookup(nt->name);

	    if (!fi)
	      {
		fi = function_add(nt->name, NULL, NULL);
	      }

	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->tcall));
	    bc->tcall.ncallargs = nt->ncallargs;
	    bc->tcall.numargs = nt->numargs;

	    if (fi->compiled_address)
	      {
		bc->tcall.op   = TAILCALLCP;
		bc->tcall.addr = (int) fi->compiled_address;
	      }
	    else if (fi->bytecode_address)
	      {
		bc->tcall.op   = TAILCALLBC;
		bc->tcall.addr = (int) fi->bytecode_address;
	      }
	    else 
	      {
		bc->tcall.op   = TAILCALL;
		bc->tcall.addr = fi->id;
	      }

	    break;
	  }

	case STACKALLOC:
	  {
	    op1arg8 *r = (op1arg8 *) op;
	    op += sizeof(op1arg8);

	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->op1arg8));
	    
	    bc->op1arg8.op  = r->op;
	    bc->op1arg8.arg = r->arg;
	  }
	  break;

	case SPAWN:
	  {
	    named_spawn *s = (named_spawn *) op;

	    op = s->name;
	    while (*op++); //skip string

	    struct function_info *fi = function_lookup(s->name);
	    
	    if (!fi)
	      {
		fi = function_add(s->name, NULL, NULL);
	      }
	    
	    bc = (yjk_bcmem *) msp_alloc(&mem, sizeof(bc->spawn));
	    bc->spawn.op  = s->op;
	    bc->spawn.func_id  = fi->id;
	    break;
	  }
	      
	case END:
	  func_end = 1;
	  break;

	case DEFUN:
	  printf("Error in bytecode\n");
	  exit(1);
	  break;

	default:
	  printf("Unrecognized opcode: 0x%x\n", *op);
	  exit(1);
	  break;
	}
    }

  // resolve jump points
  struct jump_entry *temp = unresolved;
  while (temp)
    {
      struct jump_entry *old_temp = temp;
      if (temp->id == -1) 
	{
	  temp = temp->next;
	  free(old_temp);
	  continue;
	}

      if (temp->id >= label_table_size || label_table[temp->id] == -1)
	{
	  printf("ERROR: Unresolved jump point in byte code function: %s\n", fname);
	}

      int *j = (int *) ((void *) &mem->data + temp->offset);
      *j = label_table[temp->id];

      temp = temp->next;
      free(old_temp);
    }

  //Now we're done with the label table
  free(label_table);
  
  //Add this function to lookup table
  struct function_info *fi = function_lookup(fname);

  if (!fi)
    {
      function_add(fname, &mem->data, NULL); 
    }
  else
    {
      fi->bytecode_address = &mem->data;
    }

  free(fname);
  return mem->data;
}



void *
load_bytecode(char *filename)
{

  struct bytefile_header header;

  FILE *f = fopen((const char *) filename, "rb");

  if (!f) 
    {
      printf("Can't open byte code file: %s\n", filename);
      exit(1);
    }

  if(!fread(&header, sizeof(struct bytefile_header), 1, f))
    {
      printf("Can't read byte code header: %s\n", strerror(errno));
      exit(1);
    }

  
  header.function_table = ntohl(header.function_table);
  header.bytecode = ntohl(header.bytecode);
  header.ft_length = ntohl(header.ft_length);
  header.bc_length = ntohl(header.bc_length);


  unsigned int *function_table = malloc(header.ft_length);
  char *code = malloc(header.bc_length);

  if (!function_table || !code)
    {
      printf("Couldn't allocate memory for byte file: %s\n", filename);
      exit(1);
    }

  fseek(f, header.function_table, SEEK_SET);
  if (!fread(function_table, header.ft_length, 1, f))
    {
      printf("Error [%d]: %s\n", errno, strerror(errno));
      exit(1);
    }

  fseek(f, header.bytecode, SEEK_SET);
  fread(code, header.bc_length, 1, f);

  unsigned int num_funcs = header.ft_length / 4; // 4 bytes per offset


  //Load all functions, one at a time
  int i;
  for (i = 0; i < num_funcs; i++)
    {
      load_function(code + ntohl(function_table[i]));
    }

  print_functions();

  free(function_table);
  free(code);

  return 0x0;
}



void
load_main_lib()
{
  function_add("main.print", NULL, &main_print); 

  return;
}
